package com.icinfo.cloud.provider.punish.common.publicsecurityentrust.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.icinfo.cloud.common.core.page.PageResponse;
import com.icinfo.cloud.provider.common.constant.Constant;
import com.icinfo.cloud.provider.common.exception.BusinessException;
import com.icinfo.cloud.provider.common.utils.DateUtil;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.dto.*;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.mapper.CasePublicSecurityEntrustDocumentNumberMapper;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.model.CasePublicSecurityEnforcementCode;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.model.CasePublicSecurityEntrustDocumentNumber;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.model.CasePublicSecurityEntrustDocumentNumberSegment;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.service.ICasePublicSecurityEnforcementCodeService;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.service.ICasePublicSecurityEntrustDocumentNumberSegmentService;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.service.ICasePublicSecurityEntrustDocumentNumberService;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.service.MemberApiService;
import com.icinfo.cloud.provider.punish.common.publicsecurityentrust.vo.*;
import com.icinfo.cloud.provider.punish.common.user.service.IMemberUserCommonService;
import com.icinfo.cloud.xzcf.provider.member.userinfo.dto.UserDTO;
import com.icinfo.framework.core.service.support.MyBatisServiceSupport;
import com.icinfo.framework.mybatis.mapper.entity.Example;
import com.icinfo.framework.mybatis.pagehelper.PageHelper;
import com.icinfo.framework.tools.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 公安委托文号相关配置信息操作Service实现类
 *
 * @author HKxxx
 * @date 2024/1/2 10:51
 */
@Slf4j
@Service
public class CasePublicSecurityEntrustDocumentNumberServiceImpl extends MyBatisServiceSupport implements ICasePublicSecurityEntrustDocumentNumberService {

    @Resource
    private CasePublicSecurityEntrustDocumentNumberMapper casePublicSecurityEntrustDocumentNumberMapper;

    @Resource
    private MemberApiService memberService;

    @Resource
    private ICasePublicSecurityEntrustDocumentNumberSegmentService casePublicSecurityEntrustDocumentNumberSegmentService;

    @Resource
    private ICasePublicSecurityEnforcementCodeService casePublicSecurityEnforcementCodeService;

    @Resource
    private IMemberUserCommonService memberUserCommonService;

    @Resource
    private RedissonClient redisson;

    @Value("${ding.commonDingSendUrl}")
    private String commonDingSendUrl;

    /**
     * 公安专用角色
     */
    final private static String PUBLIC_SECURITY_ROLE_ID = "3N42W5F5C2543DD696C73GB347530845";
    /**
     * 文号提醒抬头
     */
    final private static String TITLE = "文书号段不足提醒";
    /**
     * 文号提醒内容
     */
    final private static String CONTENT = "您好，%s委托执法的文书号段配置中，%s号段已不足%s个，请部门管理员尽快配置新的文书号段";
    /**
     * 公安委托获取文号-锁前缀
     */
    private static final String LOCKPREFIX = "GAWT:DOCNUM:";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String save(PublicSecurityDocumentNumberReqDTO reqDTO) {
        UserDTO userDTO = getUser();
        //保存校验
        saveCheck(reqDTO, userDTO);
        //插入主号段信息
        CasePublicSecurityEntrustDocumentNumber mainDocumentNumber = insertMainDocumentNumber(reqDTO, userDTO);
        //插入子号段信息
        insertDocumentNumberSegments(reqDTO.getSegments(), mainDocumentNumber);
        return mainDocumentNumber.getId();
    }

    /**
     * 插入子号段信息
     *
     * @param segments           子号段集合
     * @param mainDocumentNumber 主号段信息
     */
    private void insertDocumentNumberSegments(List<PublicSecurityDocumentNumberSegmentSaveDTO> segments, CasePublicSecurityEntrustDocumentNumber mainDocumentNumber) {
        List<CasePublicSecurityEntrustDocumentNumberSegment> segmentList = segments.stream()
                .map(segment -> new CasePublicSecurityEntrustDocumentNumberSegment()
                        .setId(StringUtils.uuid())
                        .setDocumentNumberId(mainDocumentNumber.getId())
                        .setEntrustAreaCode(mainDocumentNumber.getAreaCode())
                        .setEntrustOrgCode(mainDocumentNumber.getOrgCode())
                        .setOrgCode(segment.getOrgCode())
                        .setNumberStart(segment.getNumberStart())
                        .setNumberEnd(segment.getNumberEnd())
                        .setNumberSegment(segment.getNumberSegment())
                        .setUsedNumberCount(0)
                        .setIsDeleted(Constant.STRING_0)
                        .setCreateTime(mainDocumentNumber.getCreateTime())
                        .setUpdateTime(mainDocumentNumber.getUpdateTime())
                        .setCreateUser(mainDocumentNumber.getCreateUser())
                        .setCreateUserId(mainDocumentNumber.getCreateUserId())
                        .setUpdateUser(mainDocumentNumber.getUpdateUser())
                        .setUpdateUserId(mainDocumentNumber.getUpdateUserId())).collect(Collectors.toList());

        casePublicSecurityEntrustDocumentNumberSegmentService.insertBatch(segmentList);
    }

    /**
     * 插入主号段信息
     *
     * @param reqDTO  号段保存对象
     * @param userDTO 用户信息
     * @return 返回数据
     */
    private CasePublicSecurityEntrustDocumentNumber insertMainDocumentNumber(PublicSecurityDocumentNumberReqDTO reqDTO, UserDTO userDTO) {
        // 获取执法编码信息
        CasePublicSecurityEnforcementCode enforcementCode =
                casePublicSecurityEnforcementCodeService.getCasePublicSecurityEnforcementCodeByOrgCode(userDTO.getOrgCode());
        CasePublicSecurityEntrustDocumentNumber mainDocumentNumber = new CasePublicSecurityEntrustDocumentNumber();
        Date now = new Date();
        String currentUserId = String.valueOf(userDTO.getUserId());
        mainDocumentNumber.setId(StringUtils.uuid())
                .setOrgCode(userDTO.getOrgCode())
                .setAreaCode(userDTO.getAreaCode())
                .setAlarmNumber(reqDTO.getAlarmNumber())
                .setNumberStart(reqDTO.getNumberStart())
                .setNumberEnd(reqDTO.getNumberEnd())
                .setNumberSegment(reqDTO.getNumberSegment())
                .setEnforcementCode(enforcementCode.getEnforcementCode())
                .setIsDeleted(Constant.STRING_0)
                .setCreateTime(now)
                .setUpdateTime(now)
                .setCreateUser(userDTO.getRealName())
                .setCreateUserId(currentUserId)
                .setUpdateUser(userDTO.getRealName())
                .setUpdateUserId(currentUserId);
        //新增主号段信息
        casePublicSecurityEntrustDocumentNumberMapper.insert(mainDocumentNumber);
        return mainDocumentNumber;
    }

//    private String convertAreaCodeForDZ(String areaCode) {
//        // 如果是大足区的行政区划，则需要转换为重庆市的行政区划
//        if (StrUtil.isNotBlank(areaCode) && areaCode.startsWith("500111")) {
//            return "501100000000";
//        }
//        return areaCode;
//    }

    /**
     * 保存校验
     *
     * @param reqDTO  保存对象
     * @param userDTO 用户对象
     */
    private void saveCheck(PublicSecurityDocumentNumberReqDTO reqDTO, UserDTO userDTO) {
        reqDTO.verify();
        //校验大号段是否存在交集
        PublicSecurityDocumentNumberVerifyDTO verifyDto = new PublicSecurityDocumentNumberVerifyDTO();
        verifyDto.setOrgCode(userDTO.getOrgCode())
                .setNumberStart(reqDTO.getNumberStart())
                .setNumberEnd(reqDTO.getNumberEnd());
        verifyMainDocumentNumber(verifyDto);
        //校验拆分号段是否部门重复
        List<PublicSecurityDocumentNumberSegmentSaveDTO> segments = reqDTO.getSegments();
        checkSegmentsOrgCodeRepeat(reqDTO, segments);
        //校验号段是否连续
        checkSegmentsContinuous(segments);
        //校验号段数据是否等于总号段数
        checkSegmentNumbers(reqDTO, segments);
    }

    /**
     * 校验号段是否连续
     *
     * @param segments 拆分号段集合
     */
    private static void checkSegmentsContinuous(List<PublicSecurityDocumentNumberSegmentSaveDTO> segments) {
        segments.sort((o1, o2) -> (int) (Long.parseLong(o1.getNumberStart()) - Long.parseLong(o2.getNumberStart())));
        PublicSecurityDocumentNumberSegmentSaveDTO upSegment = null;
        for (PublicSecurityDocumentNumberSegmentSaveDTO segment : segments) {
            if (Objects.isNull(upSegment)) {
                upSegment = segment;
                continue;
            }
            //上一号段结束
            long upEnd = Long.parseLong(upSegment.getNumberEnd());
            long start = Long.parseLong(segment.getNumberStart());
            if (start != upEnd + 1) {
                throw new BusinessException("拆分号段不连续，请检查后重新提交");
            }
            upSegment = segment;
        }
    }

    /**
     * 校验号段数据是否等于总号段数
     *
     * @param reqDTO   公安委托文号配置请求对象
     * @param segments 拆分号段集合
     */
    private static void checkSegmentNumbers(PublicSecurityDocumentNumberReqDTO reqDTO, List<PublicSecurityDocumentNumberSegmentSaveDTO> segments) {
        BigDecimal segmentsSum = segments.stream().map(t -> BigDecimal.valueOf(t.getNumberSegment())).reduce(BigDecimal.ZERO, BigDecimal::add);
        //获取大号段总文号数
        BigDecimal allDocumentNumberSum = BigDecimal.valueOf(reqDTO.getNumberSegment());
        if (!segmentsSum.equals(allDocumentNumberSum)) {
            throw new BusinessException("拆分号段数与总号段数不相等，请检查后重新提交");
        }
    }

    /**
     * 校验拆分号段是否部门重复
     *
     * @param reqDTO   公安委托文号配置请求对象
     * @param segments 拆分号段集合
     */
    private void checkSegmentsOrgCodeRepeat(PublicSecurityDocumentNumberReqDTO reqDTO, List<PublicSecurityDocumentNumberSegmentSaveDTO> segments) {
        List<String> orgCodes = segments.stream()
                .map(PublicSecurityDocumentNumberSegmentSaveDTO::getOrgCode).distinct().collect(Collectors.toList());
        if (!Objects.equals(orgCodes.size(), reqDTO.getSegments().size())) {
            throw new BusinessException("存在重复的部门编码，请检查后重新提交");
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(String id) {
        //删除校验
        deleteCheck(id);
        //删除主号段信息
        removeMainDocumentNumberById(id);
        //删除子号段信息
        casePublicSecurityEntrustDocumentNumberSegmentService.removeByDocumentNumberId(id);
    }

    /**
     * 删除校验
     *
     * @param id 主号段id
     */
    private void deleteCheck(String id) {
        UserDTO user = getUser();
        CasePublicSecurityEntrustDocumentNumber documentNumber = new CasePublicSecurityEntrustDocumentNumber();
        documentNumber.setId(id);
        documentNumber.setIsDeleted(Constant.STRING_0);
        documentNumber.setOrgCode(user.getOrgCode());
        documentNumber.setAreaCode(user.getAreaCode());
        documentNumber = casePublicSecurityEntrustDocumentNumberMapper.selectOne(documentNumber);
        if (Objects.isNull(documentNumber)) {
            throw new BusinessException("不存在号段信息");
        }
        //校验号段是否已经使用
        List<CasePublicSecurityEntrustDocumentNumberSegment> segments = casePublicSecurityEntrustDocumentNumberSegmentService.listByDocumentNumberId(id);
        if (CollUtil.isEmpty(segments)) {
            return;
        }
        BigDecimal usedCount = segments.stream().map(t -> BigDecimal.valueOf(t.getUsedNumberCount())).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (usedCount.compareTo(BigDecimal.ZERO) > 0) {
            throw new BusinessException("该号段已经被使用，无法删除！");
        }
    }

    /**
     * 删除主号段数据
     *
     * @param id 主号段id
     */
    private void removeMainDocumentNumberById(String id) {
        UserDTO user = getUser();
        CasePublicSecurityEntrustDocumentNumber documentNumber = new CasePublicSecurityEntrustDocumentNumber();
        documentNumber.setId(id);
        documentNumber.setIsDeleted(Constant.STRING_1);
        documentNumber.setUpdateTime(new Date());
        documentNumber.setUpdateUserId(String.valueOf(user.getUserId()));
        documentNumber.setUpdateUser(user.getPhone());
        casePublicSecurityEntrustDocumentNumberMapper.updateByPrimaryKeySelective(documentNumber);
    }

    @Override
    public PageResponse<PublicSecurityDocumentNumberVO> pageList(PublicSecurityDocumentNumberPageDTO dto) {
        UserDTO user = getUser();
        // 获取大段号段信息列表
        List<CasePublicSecurityEntrustDocumentNumber> mains = getMainDocumentNumberList(dto, user);
        if (CollUtil.isEmpty(mains)) {
            return PageResponse.empty();
        }
        // 得到街道配置信息
        List<String> mainIds = mains.stream().map(CasePublicSecurityEntrustDocumentNumber::getId).collect(Collectors.toList());
        // 根据大段号段id集合获取分组的街道号段信息
        Map<String, Long> groupUsedNumberCount = casePublicSecurityEntrustDocumentNumberSegmentService.groupCountDocumentNumberSegment(mainIds);
        // 获取机构名称
        List<String> orgCodes = mains.stream().map(CasePublicSecurityEntrustDocumentNumber::getOrgCode).distinct().collect(Collectors.toList());
        Map<String, MemberOrgVo> orgNames = memberService.getOrgNameByOrgCode(orgCodes);
        PageResponse<CasePublicSecurityEntrustDocumentNumber> pageResponse = new PageResponse<>(mains);
        return pageResponse.convert(info -> handleDocumentNumberPageVo(info, groupUsedNumberCount, orgNames, user.getOrgCode()));
    }

    /**
     * 获取大段号段信息列表
     */
    private List<CasePublicSecurityEntrustDocumentNumber> getMainDocumentNumberList(PublicSecurityDocumentNumberPageDTO dto, UserDTO user) {
        PageHelper.startPage(dto.getPageNum(), dto.getLength(), "create_time desc");
        Example mainExample = new Example(CasePublicSecurityEntrustDocumentNumber.class);
        Example.Criteria mainCriteria = mainExample.createCriteria();
        mainCriteria.andEqualTo("isDeleted", Constant.STRING_0);
        // 号段创建时间
        if (!Objects.isNull(dto.getStartTime()) && !Objects.isNull(dto.getEndTime())) {
            mainCriteria.andBetween("createTime", DateUtil.getStartTime(dto.getStartTime()), DateUtil.getEndTime(dto.getEndTime()));
        }
        // 限制区划
        limitArea(dto, user, mainCriteria);
        // 得到大段信息
        return casePublicSecurityEntrustDocumentNumberMapper.selectByExample(mainExample);
    }


    /**
     * 限制区划
     */
    private void limitArea(PublicSecurityDocumentNumberPageDTO dto, UserDTO user, Example.Criteria mainCriteria) {
        String areaCode = user.getAreaCode();
        if (Constant.STRING_1.equals(dto.getEnforceRegiLevel())) {
            if (StrUtil.isNotBlank(dto.getOrgCode())) {
                mainCriteria.andEqualTo("orgCode", dto.getOrgCode());
            } else {
                mainCriteria.andLike("areaCode", "%" + areaCode.substring(0, 2) + "%");
            }
        } else {
            mainCriteria.andEqualTo("orgCode", user.getOrgCode());
        }
    }

    /**
     * 处理分页返回参数
     */
    private PublicSecurityDocumentNumberVO handleDocumentNumberPageVo(CasePublicSecurityEntrustDocumentNumber info,
                                                                      Map<String, Long> groupUsedNumberCount,
                                                                      Map<String, MemberOrgVo> orgNames,
                                                                      String userOrgCode) {
        PublicSecurityDocumentNumberVO vo = new PublicSecurityDocumentNumberVO();
        vo.setId(info.getId());
        MemberOrgVo org = MapUtil.get(orgNames, info.getOrgCode(), MemberOrgVo.class);
        vo.setOrgName(Objects.nonNull(org) ? org.getOrgShortName() : "");
        vo.setNumberSegment(String.valueOf(info.getNumberSegment()));
        // 街道已使用的数量
        long sum = groupUsedNumberCount.get(info.getId());
        vo.setUsed(String.valueOf(sum));
        long surplus = info.getNumberSegment() - sum;
        vo.setSurplusNumber(String.valueOf(surplus));
        // 补齐文号
        vo.setSerialNumberStart(convertDocumentNumber(info.getEnforcementCode().substring(0, 6), info.getNumberStart()));
        vo.setSerialNumberEnd(convertDocumentNumber(info.getEnforcementCode().substring(0, 6), info.getNumberEnd()));
        vo.setCreateTime(info.getCreateTime());
        // 判断按钮权限
        if (Objects.equals(userOrgCode, info.getOrgCode()) && sum == 0L) {
            //文号未使用过，并且当前用户为该文号归属机构时，可以操作数据
            vo.setButton(true);
        }
        return vo;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String getPublicSecurityDocumentNumber(String orgCode) {
        if (StrUtil.isBlank(orgCode)) {
            throw new BusinessException("委托部门orgCode不能为空");
        }
        UserDTO user = getUser();
        String userOrgCode = user.getOrgCode();
        RLock lock = redisson.getLock(LOCKPREFIX + orgCode + ":" + userOrgCode);
        try {
            if (!lock.tryLock(500L, TimeUnit.MILLISECONDS)) {
                throw new BusinessException("获取锁失败!");
            }
            CasePublicSecurityEntrustDocumentNumberSegment segment =
                    casePublicSecurityEntrustDocumentNumberSegmentService.getRemainDocumentNumberInfo(orgCode, userOrgCode);
            if (Objects.isNull(segment)) {
                throw new BusinessException("无可用文号，请添加可用文号段！");
            }
            //文号自增并获取
            return documentNumberIncrementAndGet(segment);
        } catch (Exception e) {
            log.error("获取公安委托文号失败", e);
            throw new BusinessException(String.format("获取公安委托文号失败：%s", e.getMessage()));
        } finally {
            lock.unlock();
        }
    }

    /**
     * 文号自增并获取
     *
     * @param segment 子号段信息
     * @return 获取的文号
     */
    private String documentNumberIncrementAndGet(CasePublicSecurityEntrustDocumentNumberSegment segment) {
        // 获取大段信息
        CasePublicSecurityEntrustDocumentNumber documentNumberDetails =
                casePublicSecurityEntrustDocumentNumberMapper.selectByPrimaryKey(segment.getDocumentNumberId());
        if (Objects.isNull(documentNumberDetails)) {
            throw new BusinessException("无公安委托文号主表配置！");
        }
        String subEnforcementCode = documentNumberDetails.getEnforcementCode().substring(0, 6);
        //如果还未使用过该号段则从开始号码取
        long serialNumber = StrUtil.isBlank(segment.getCurrentNumber()) ?
                Long.parseLong(segment.getNumberStart()) : Long.parseLong(segment.getCurrentNumber()) + 1;
        // 左侧补0
        String fillNumber = fill(serialNumber, 9, 9);
        // 对号码取余
        String documentNumber = convertDocumentNumber(subEnforcementCode, fillNumber);
        // 更新文号信息
        segment.setCurrentNumber(String.valueOf(fillNumber));
        segment.setUsedNumberCount(segment.getUsedNumberCount() + 1);
        if (1 != casePublicSecurityEntrustDocumentNumberSegmentService.updateBySegmentSelective(segment)) {
            throw new BusinessException("公安委托街道文号配置更新失败！");
        }
        return documentNumber;
    }


    /**
     * description: 格式化数字，实现左侧补 0.
     *
     * @param num 格式化的数字
     * @param min 最小位数
     * @param max 最大位数
     */
    public String fill(long num, int min, int max) {
        NumberFormat numberFormat = NumberFormat.getInstance();
        // 禁用数字格式化分组。 如：  000,001
        numberFormat.setGroupingUsed(false);
        // 保留最小位数
        numberFormat.setMinimumIntegerDigits(min);
        // 保留最大位数
        numberFormat.setMaximumIntegerDigits(max);
        return numberFormat.format(num);
    }


    @Override
    public Boolean hasRemainNumber(String orgCode) {
        UserDTO user = getUser();
        return Objects.nonNull(casePublicSecurityEntrustDocumentNumberSegmentService.getRemainDocumentNumberInfo(orgCode, user.getOrgCode()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void publicSecurityRollbackDocumentNumber(PublicSecurityDocumentNumberRollbackDTO queryDTO) {
        log.info("publicSecurityRollbackDocumentNumber -> queryDTO:{}", queryDTO.toString());
        UserDTO user = getUser();
        String documentNumber = queryDTO.getDocumentNumber().substring(6, 15);
        // 找到该文号所属的号段信息
        CasePublicSecurityEntrustDocumentNumberSegment segment = casePublicSecurityEntrustDocumentNumberSegmentService
                .getDocumentNumberSegment(documentNumber, queryDTO.getOrgCode(), user.getOrgCode());
        if (Objects.isNull(segment)) {
            throw new BusinessException("公安委托文号回滚失败：无对应街道号段信息");
        }
        rollbackDocumentNumber(segment, documentNumber);
    }

    /**
     * 回滚文号
     *
     * @param rollback       需要回滚的具体街道配置信息
     * @param documentNumber 本次回滚的文号（中间九位）
     */
    private void rollbackDocumentNumber(CasePublicSecurityEntrustDocumentNumberSegment rollback, String documentNumber) {
        // 得到该文号配置的使用情况
        UsageSituationVo usageSituationVo = casePublicSecurityEntrustDocumentNumberSegmentService.usageSituation(rollback);
        // 已使用数
        long used = usageSituationVo.getUsed();
        // 如果已使用数回归之后小于等于0  那么设置已使用到的号段为null
        rollback.setCurrentNumber(null);
        if ((used - 1) > 0) {
            // 已使用到的数字
            long currentLong = Long.parseLong(rollback.getCurrentNumber());
            // 本次回归的数字
            long documentNumberLong = Long.parseLong(documentNumber);
            if (currentLong != documentNumberLong) {
                throw new BusinessException("rollbackDocumentNumber -> error：" + "本次公安委托文号回滚存在重复文号问题！");
            }
            // 需要回归到的数字
            long rollbackNumber = currentLong - 1;
            // 将使用的文号-1 再左侧补0
            String fillNumber = fill(rollbackNumber, 9, 9);
            rollback.setCurrentNumber(fillNumber);
        }
        // 将已使用数量 - 1
        rollback.setUsedNumberCount(rollback.getUsedNumberCount() - 1);
        int i = casePublicSecurityEntrustDocumentNumberSegmentService.updateBySegment(rollback);
        if (i == 1) {
            log.info("公安委托文号回归成功！");
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void residualNumberWarning(PublicSecurityDocumentNumberRollbackDTO queryDTO) {
        UserDTO user = getUser();
        log.info("residualNumberWarning -> queryDTO:{}", queryDTO.toString());
        // 需要将文号进行拆解
        String documentNumber = queryDTO.getDocumentNumber().substring(6, 15);
        // 得到该文号对应的配置信息
        CasePublicSecurityEntrustDocumentNumberSegment segment = casePublicSecurityEntrustDocumentNumberSegmentService
                .getDocumentNumberSegment(documentNumber, queryDTO.getOrgCode(), user.getOrgCode());
        if (Objects.isNull(segment)) {
            throw new BusinessException("该部门不存在对应的街道文号配置信息");
        }
        // 获取当前日期的开始时间和结束时间
        long startTime = DateUtil.getStartTime().getTime();
        if (Objects.nonNull(segment.getAlarmTime()) && segment.getAlarmTime().getTime() >= startTime) {
            // 报警时间不为空 或 报警时间在当天的开始日期之厚 则不需要报警
            return;
        }
        CasePublicSecurityEntrustDocumentNumber documentNumberDetail = casePublicSecurityEntrustDocumentNumberMapper.selectByPrimaryKey(segment.getDocumentNumberId());
        // 当前大段的报警阈值
        long alarmNumber = documentNumberDetail.getAlarmNumber();
        // 获取到委托部门的名称
        Map<String, MemberOrgVo> orgNameMap = memberService.getOrgNameByOrgCode(Collections.singletonList(queryDTO.getOrgCode()));
        MemberOrgVo orgVo = MapUtil.get(orgNameMap, queryDTO.getOrgCode(), MemberOrgVo.class);
        // 获取到街道的名称
        Map<String, MemberOrgVo> deptNameMap = memberService.getOrgNameByOrgCode(Collections.singletonList(user.getOrgCode()));
        MemberOrgVo deptVo = MapUtil.get(deptNameMap, segment.getOrgCode(), MemberOrgVo.class);
        // 部门编码
        String orgCode = queryDTO.getOrgCode();
        // 获取当前部门所有未用完的文号信息
        long total = casePublicSecurityEntrustDocumentNumberSegmentService.getUnusedTotal(orgCode, user.getOrgCode(), documentNumberDetail.getId());
        if (total > alarmNumber) {
            return;
        }
        //发送报警消息
        alarm(segment, queryDTO.getOrgCode(), orgVo.getOrgShortName(), deptVo.getOrgShortName(), total);
    }


    @Override
    public PublicSecurityDocumentNumberDetailsVo details(String id) {
        if (StrUtil.isBlank(id)) {
            throw new BusinessException("id不能为空！");
        }
        // 得到大段信息
        CasePublicSecurityEntrustDocumentNumber documentNumber = casePublicSecurityEntrustDocumentNumberMapper.selectByPrimaryKey(id);
        if (Objects.isNull(documentNumber)) {
            return new PublicSecurityDocumentNumberDetailsVo();
        }
        // 需要获取名称的部门代码
        List<String> orgCodes = new ArrayList<>();
        orgCodes.add(documentNumber.getOrgCode());
        // 得到街道号段信息
        List<CasePublicSecurityEntrustDocumentNumberSegment> segments =
                casePublicSecurityEntrustDocumentNumberSegmentService.listByDocumentNumberId(documentNumber.getId());
        if (CollUtil.isNotEmpty(segments)) {
            List<String> segmentOrgCodes = segments.stream().map(CasePublicSecurityEntrustDocumentNumberSegment::getOrgCode).distinct().collect(Collectors.toList());
            orgCodes.addAll(segmentOrgCodes);
        }
        // 委托部门名称
        Map<String, MemberOrgVo> orgVos = memberService.getOrgNameByOrgCode(orgCodes);
        return handleDocumentNumberDetails(documentNumber, segments, orgVos);
    }

    @Override
    public String getEnforcementCodeByCurrentUser() {
        UserDTO userDTO = getUser();
        CasePublicSecurityEnforcementCode casePublicSecurityEnforcementCode =
                casePublicSecurityEnforcementCodeService.getCasePublicSecurityEnforcementCodeByOrgCode(userDTO.getOrgCode());
        return casePublicSecurityEnforcementCode.getEnforcementCode().substring(0, 6);
    }


    /**
     * 处理文号详情返回参数
     */
    private PublicSecurityDocumentNumberDetailsVo handleDocumentNumberDetails(CasePublicSecurityEntrustDocumentNumber info,
                                                                              List<CasePublicSecurityEntrustDocumentNumberSegment> segments,
                                                                              Map<String, MemberOrgVo> orgVos) {
        PublicSecurityDocumentNumberDetailsVo vo = new PublicSecurityDocumentNumberDetailsVo();
        vo.setId(info.getId());
        MemberOrgVo orgName = MapUtil.get(orgVos, info.getOrgCode(), MemberOrgVo.class);
        vo.setOrgName(Objects.isNull(orgName) ? "" : orgName.getOrgShortName());
        vo.setNumberSegment(String.valueOf(info.getNumberSegment()));
        // 街道已使用的数量
        long sum = segments.stream().mapToLong(CasePublicSecurityEntrustDocumentNumberSegment::getUsedNumberCount).sum();
        vo.setUsed(String.valueOf(sum));
        long surplus = info.getNumberSegment() - sum;
        vo.setSurplusNumber(String.valueOf(surplus));
        // 补齐文号
        vo.setSerialNumberStart(convertDocumentNumber(info.getEnforcementCode().substring(0, 6), info.getNumberStart()));
        vo.setSerialNumberEnd(convertDocumentNumber(info.getEnforcementCode().substring(0, 6), info.getNumberEnd()));
        vo.setCreateTime(info.getCreateTime());
        //转换详情
        vo.setDetailsVo(segments.stream()
                .map(s -> handleSegment(info.getEnforcementCode(), orgVos, s))
                .collect(Collectors.toList()));
        return vo;
    }


    private EntrustDocumentNumberSegmentDetailsVo handleSegment(String enforcementCode,
                                                                Map<String, MemberOrgVo> orgNames,
                                                                CasePublicSecurityEntrustDocumentNumberSegment s) {
        EntrustDocumentNumberSegmentDetailsVo detailsVo = new EntrustDocumentNumberSegmentDetailsVo();
        String subStrEnforcementCode = enforcementCode.substring(0, 6);
        detailsVo.setId(s.getId());
        MemberOrgVo orgName = MapUtil.get(orgNames, s.getOrgCode(), MemberOrgVo.class);
        detailsVo.setOrgName(Objects.isNull(orgName) ? "" : orgName.getOrgShortName());
        detailsVo.setNumberSegmentStart(convertDocumentNumber(subStrEnforcementCode, s.getNumberStart()));
        detailsVo.setNumberSegmentEnd(convertDocumentNumber(subStrEnforcementCode, s.getNumberEnd()));
        detailsVo.setNumberSegment(s.getNumberSegment());
        detailsVo.setCurrentNumber(StrUtil.isBlank(s.getCurrentNumber()) ? "" : convertDocumentNumber(subStrEnforcementCode, s.getCurrentNumber()));
        detailsVo.setUsedNumberCount(s.getUsedNumberCount());
        UsageSituationVo usageSituation = casePublicSecurityEntrustDocumentNumberSegmentService.usageSituation(s);
        detailsVo.setUnusedNumber(usageSituation.getSurplus().intValue());
        return detailsVo;
    }

    @Override
    public void verifyMainDocumentNumber(PublicSecurityDocumentNumberVerifyDTO reqDTO) {
        reqDTO.verifyNumber();
        long numberStart = Long.parseLong(reqDTO.getNumberStart());
        long numberEnd = Long.parseLong(reqDTO.getNumberEnd());
        if (numberStart > numberEnd) {
            throw new BusinessException("开始号段不能大于结束号段");
        }
        PublicSecurityMaxAndMinDocumentNumberVo vo = casePublicSecurityEntrustDocumentNumberMapper.selectOrgMaxAndMinNumber(reqDTO.getOrgCode());
        if (Objects.isNull(vo) || Objects.isNull(vo.getMinNumber()) || Objects.isNull(vo.getMaxNumber())) {
            //没有数据则无需判断
            return;
        }
        if (numberStart <= vo.getMaxNumber()) {
            throw new BusinessException("号段配置错误，请重新输入。\n确保号段未与历史号段重复，且号段开始必须大于" + vo.getMaxNumber());
        }
    }


    /**
     * 发送愉快政信息
     *
     * @param segment  文号配置
     * @param orgCode  部门编码
     * @param orgName  部门名称
     * @param deptName 街道名称
     * @param total    剩余总数
     */
    private void alarm(CasePublicSecurityEntrustDocumentNumberSegment segment, String orgCode, String orgName, String deptName, long total) {
        // 获取当前部门下的公安专用角色用户愉快政id
        List<SendDingMessageVo> dingIds = memberUserCommonService.getDingIdByOrgCodeAndRoleId(orgCode, PUBLIC_SECURITY_ROLE_ID);
        if (CollUtil.isEmpty(dingIds)) {
            log.error("alarm -> orgCode:{},无系统管理员角色信息", orgCode);
            return;
        }
        String dingIdStr = dingIds.stream().map(SendDingMessageVo::getDingId).collect(Collectors.joining(","));
        if (StrUtil.isBlank(dingIdStr)) {
            log.error("alarm -> orgCode:{},系统管理员无dingId", orgCode);
            return;
        }
        sendDingMessage(String.format(CONTENT, orgName, deptName, total), dingIdStr);
        // 报警之后更新报警时间
        segment.setAlarmTime(new Date());
        casePublicSecurityEntrustDocumentNumberSegmentService.updateBySegmentSelective(segment);
    }

    /**
     * 发送信息
     *
     * @param content    内容
     * @param dingDingId 愉快政id
     */
    private void sendDingMessage(String content, String dingDingId) {
        log.info("sendDingMessage -> title:{},content:{},dingDingId：{}", TITLE, content, dingDingId);
        Map<String, Object> urlParam = new HashMap<>();
        urlParam.put("title", TITLE);
        urlParam.put("content", content);
        urlParam.put("dingDingId", dingDingId);
        String json = JSONObject.toJSONString(urlParam);
        HttpUtil.createPost(commonDingSendUrl).body(json).execute().body();
    }




    /**
     * 通过区划代码获取最终文号
     */
    private String convertDocumentNumber(String orgCode, String number) {
        long checkCode = getCheckCode(Long.parseLong(number));
        return orgCode + number + checkCode;
    }


    /**
     * 对数字进行位数求和并取余数
     */
    private long getCheckCode(long n) {
        long sum = 0;
        while (n > 0) {
            sum += n % 10;
            n /= 10;
        }
        long result = sum % 11;
        //结果中 余0和余10 尾数都为0
        return result > 9 ? 0 : result;
    }


}




